package coderx

import (
	"encoding/binary"
	"fmt"
	"gitee.com/zhongguo168a/gocodes/datax"
	"gitee.com/zhongguo168a/gocodes/datax/binaryx"
	"gitee.com/zhongguo168a/gocodes/datax/schemax"
	"gitee.com/zhongguo168a/gocodes/datax/schemax/basickind"
	"gitee.com/zhongguo168a/gocodes/myx/errorx"
	"io"
	"math"
	"strconv"
)

func NewByteToMapWithType(_type string) (obj *ByteToMap, err error) {
	schema := schemax.GetDeclByKey(_type)
	if schema == nil {
		err = errorx.New("schema not found: ", datax.M{"_type": _type})
		return
	}
	obj = NewByteToMapWithSchema(schema.(*schemax.ClassDecl))
	return
}

func NewByteToMapWithSchema(schema *schemax.ClassDecl) (obj *ByteToMap) {
	obj = NewByteToMap()
	obj.schema = schema

	return
}

func NewByteToMap() (obj *ByteToMap) {
	obj = &ByteToMap{}
	obj.endian = binary.LittleEndian
	return
}

type ByteToMap struct {
	schema *schemax.ClassDecl
	//
	change bool
	//
	endian binary.ByteOrder
}

func (coder *ByteToMap) Reset() {
	coder.schema = nil
	coder.endian = binary.LittleEndian
	coder.change = false
}

func (coder *ByteToMap) SetByteOrder(order binary.ByteOrder) *ByteToMap {
	coder.endian = order
	return coder
}

func (coder *ByteToMap) Read(reader io.Reader, target map[string]interface{}) (err error) {
	if target == nil {
		err = errorx.New("target is nil")
		return
	}
	change, readerr := binaryx.ReadBool(reader, coder.endian)
	if readerr != nil {
		err = errorx.Wrap(readerr, fmt.Sprintf("read change"))
		return
	}
	coder.change = change
	return coder.decodeAllObj(target, reader, coder.schema)
}

// keyMode 0-source的字段, 1-所有字段
func (coder *ByteToMap) decodeAllObj(targetMap map[string]interface{}, reader io.Reader, schema schemax.IDecl) (err error) {
	var (
		decl = schema.(*schemax.ClassDecl)
	)
	allfields := decl.GetAllField()
	if coder.change {
		var newfields []*schemax.Field
		groupLen := int(math.Ceil(float64(len(allfields)) / 8.0))
		for i := 0; i < groupLen; i++ {
			state, readerr := binaryx.ReadUint8(reader, coder.endian)
			if readerr != nil {
				err = errorx.Wrap(readerr, fmt.Sprintf("read state"))
				return
			}
			for j := 0; j < 8; j++ {
				if binaryx.GetState(uint(state), 1<<j) {
					index := i*8 + j
					newfields = append(newfields, allfields[index])
				}
			}
		}

		allfields = newfields
	}

	for _, field := range allfields {
		fname := field.Name
		switch ftyp := field.Type.(type) {
		case *schemax.ClassType:
			st, derr := coder.decodeAllClass(reader, ftyp.Decl)
			if derr != nil {
				err = errorx.Wrap(derr, fname)
				return
			}
			targetMap[fname] = st
		case *schemax.ArrayType:
			data, derr := coder.decodeAllSlice(reader, fname, ftyp)
			if derr != nil {
				err = errorx.Wrap(derr, fname)
				return
			}
			targetMap[fname] = data
		case *schemax.MapType:
			data, derr := coder.decodeAllMap(reader, ftyp)
			if derr != nil {
				err = errorx.Wrap(derr, fname)
				return
			}
			targetMap[fname] = data
		case *schemax.EnumType:
			data, derr := coder.decodeAllEnum(reader, ftyp.Decl)
			if derr != nil {
				err = errorx.Wrap(derr, fname)
				return
			}
			targetMap[fname] = data
		case *schemax.BasicType:
			data, derr := coder.decodeAllBasic(reader, ftyp.Kind)
			if derr != nil {
				err = errorx.Wrap(derr, fname)
				return
			}
			targetMap[fname] = data
		case *schemax.AnyType:
			continue
		default:
		}
	}
	return
}

func (coder *ByteToMap) decodeAllClass(reader io.Reader, decl string) (st map[string]interface{}, err error) {
	isNil, readerr := binaryx.ReadBool(reader, coder.endian)
	if readerr != nil {
		err = errorx.Wrap(readerr, fmt.Sprintf("read class nil"))
		return
	}
	if isNil {
		return
	}
	stdesc := schemax.GetDeclByKey(decl)
	if stdesc == nil {
		err = errorx.New("schema not found: ", datax.M{"decl": decl})
		return
	}

	st = map[string]interface{}{}
	err = coder.decodeAllObj(st, reader, stdesc)
	return
}
func (coder *ByteToMap) decodeAllEnum(reader io.Reader, decl string) (data interface{}, err error) {
	idesc := schemax.GetDeclByKey(decl)
	if idesc == nil {
		err = errorx.New("schema not found", datax.M{"decl": decl})
		return
	}

	enumdesc := idesc.(*schemax.EnumDecl)
	data, err = coder.decodeAllBasic(reader, basickind.Kind(enumdesc.Kind))
	if err != nil {
		return
	}
	return
}

func (coder *ByteToMap) decodeAllSlice(reader io.Reader, fname string, ftyp *schemax.ArrayType) (arr []interface{}, err error) {
	ilen, readerr := binaryx.ReadInt16(reader, coder.endian)
	if readerr != nil {
		err = errorx.Wrap(readerr, fmt.Sprintf("read array length"))
		return
	}
	mlen := int(ilen)
	if mlen == 0 {
		return
	}
	switch etyp := ftyp.Elem.(type) {
	case *schemax.ClassType:
		for i := 0; i < mlen; i++ {
			st, derr := coder.decodeAllClass(reader, etyp.Decl)
			if derr != nil {
				err = errorx.Wrap(derr, strconv.Itoa(i))
				return
			}
			arr = append(arr, st)
		}
	case *schemax.BasicType:
		for i := 0; i < mlen; i++ {
			val, derr := coder.decodeAllBasic(reader, etyp.Kind)
			if derr != nil {
				err = errorx.Wrap(derr, strconv.Itoa(i))
				return
			}
			arr = append(arr, val)
		}
	case *schemax.EnumType:
		for i := 0; i < mlen; i++ {
			val, derr := coder.decodeAllEnum(reader, etyp.Decl)
			if derr != nil {
				err = errorx.Wrap(derr, strconv.Itoa(i))
				return
			}
			arr = append(arr, val)
		}
	default:
		err = errorx.New("decode array: not support type: " + etyp.String())
		return

	}

	return
}

func (coder *ByteToMap) decodeAllMap(reader io.Reader, ftyp *schemax.MapType) (mmap map[string]interface{}, err error) {
	isNil, nilerr := binaryx.ReadBool(reader, coder.endian)
	if nilerr != nil {
		err = errorx.Wrap(nilerr, fmt.Sprintf("read map nil"))
		return
	}
	if isNil {
		return
	}

	mlen, nilerr := binaryx.ReadInt16(reader, coder.endian)
	if nilerr != nil {
		err = errorx.Wrap(nilerr, fmt.Sprintf("read map length"))
		return
	}

	mmap = map[string]interface{}{}
	for i := 0; i < int(mlen); i++ {
		mkey, readerr := binaryx.ReadUTF(reader, coder.endian)
		if readerr != nil {
			err = errorx.Wrap(readerr, fmt.Sprintf("read map key"))
			return
		}

		switch etyp := ftyp.Value.(type) {
		case *schemax.ClassType:
			st, derr := coder.decodeAllClass(reader, etyp.Decl)
			if derr != nil {
				err = errorx.Wrap(derr, mkey)
				return
			}
			mmap[mkey] = st
		case *schemax.BasicType:
			val, derr := coder.decodeAllBasic(reader, etyp.Kind)
			if derr != nil {
				err = errorx.Wrap(derr, mkey)
				return
			}
			mmap[mkey] = val
		case *schemax.EnumType:
			val, derr := coder.decodeAllEnum(reader, etyp.Decl)
			if derr != nil {
				err = errorx.Wrap(derr, mkey)
				return
			}
			mmap[mkey] = val
		default:
			err = errorx.New("not support type: " + etyp.String())
			return
		}
	}
	return
}

func (coder *ByteToMap) decodeAllBasic(reader io.Reader, kind basickind.Kind) (val interface{}, err error) {
	switch kind {
	case basickind.Bool:
		data, rerr := binaryx.ReadBool(reader, coder.endian)
		if rerr != nil {
			err = errorx.Wrap(rerr, fmt.Sprintf("read bool"))
			return
		}
		val = data
	case basickind.Int8:
		data, rerr := binaryx.ReadInt8(reader, coder.endian)
		if rerr != nil {
			err = errorx.Wrap(rerr, fmt.Sprintf("read int8"))
			return
		}
		val = data
	case basickind.Int16:
		data, rerr := binaryx.ReadInt16(reader, coder.endian)
		if rerr != nil {
			err = errorx.Wrap(rerr, fmt.Sprintf("read int16"))
			return
		}
		val = data
	case basickind.Int32:
		data, rerr := binaryx.ReadInt32(reader, coder.endian)
		if rerr != nil {
			err = errorx.Wrap(rerr, fmt.Sprintf("read int32"))
			return
		}
		val = data
	case basickind.Int64:
		data, rerr := binaryx.ReadInt64(reader, coder.endian)
		if rerr != nil {
			err = errorx.Wrap(rerr, fmt.Sprintf("read int64"))
			return
		}
		val = data
	case basickind.Uint8:
		data, rerr := binaryx.ReadUint8(reader, coder.endian)
		if rerr != nil {
			err = errorx.Wrap(rerr, fmt.Sprintf("read uint8"))
			return
		}
		val = data
	case basickind.Uint16:
		data, rerr := binaryx.ReadUint8(reader, coder.endian)
		if rerr != nil {
			err = errorx.Wrap(rerr, fmt.Sprintf("read uint16"))
			return
		}
		val = data
	case basickind.Uint32:
		data, rerr := binaryx.ReadUint32(reader, coder.endian)
		if rerr != nil {
			err = errorx.Wrap(rerr, fmt.Sprintf("read uint32"))
			return
		}
		val = data
	case basickind.Uint64:
		data, rerr := binaryx.ReadUint64(reader, coder.endian)
		if rerr != nil {
			err = errorx.Wrap(rerr, fmt.Sprintf("read uint64"))
			return
		}
		val = data
	case basickind.Float32:
		data, rerr := binaryx.ReadFloat32(reader, coder.endian)
		if rerr != nil {
			err = errorx.Wrap(rerr, fmt.Sprintf("read float32"))
			return
		}
		val = data
	case basickind.Float64:
		data, rerr := binaryx.ReadFloat64(reader, coder.endian)
		if rerr != nil {
			err = errorx.Wrap(rerr, fmt.Sprintf("read float64"))
			return
		}
		val = data
	case basickind.String:
		data, rerr := binaryx.ReadUTF(reader, coder.endian)
		if rerr != nil {
			err = errorx.Wrap(rerr, fmt.Sprintf("read string"))
			return
		}
		val = data
	case basickind.Bytes:
		data, rerr := binaryx.ReadBytes(reader, coder.endian)
		if rerr != nil {
			err = errorx.Wrap(rerr, fmt.Sprintf("read bytes"))
			return
		}
		val = data
	}
	return
}
